package com.donleo.alipay.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.internal.util.AlipaySignature;
import com.alipay.api.request.*;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.donleo.alipay.config.AlipayConfig;
import com.donleo.alipay.constant.Constant;
import com.donleo.alipay.dao.PayLogDao;
import com.donleo.alipay.dao.RefundLogDao;
import com.donleo.alipay.model.PayLog;
import com.donleo.alipay.model.RefundLog;
import com.donleo.alipay.service.PayLogService;
import com.donleo.alipay.utils.UUIDUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.lang.Double.parseDouble;

/**
 * @author liangd
 * @since 2021-03-06 15:08
 */
@Service
@Slf4j
public class PayLogServiceImpl extends ServiceImpl<PayLogDao, PayLog> implements PayLogService {

    @Autowired
    private PayLogDao payLogDao;

    @Autowired
    private RefundLogDao refundLogDao;

    @Override
    public void pay(String price, String name, HttpServletResponse response) {
        try {
            //获得初始化的AlipayClient
            DefaultAlipayClient client = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json",
                    AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
            //电脑网页支付
            AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
            //手机网页支付
//            AlipayTradeWapPayRequest request=new AlipayTradeWapPayRequest();
            Map<Object, Object> map = new HashMap<>();
            //商户订单号，商户网站订单系统中唯一订单号，必填
            String outTradeNo = UUIDUtil.getUUID();
            map.put("out_trade_no", outTradeNo);
            //销售产品码，目前仅支持FAST_INSTANT_TRADE_PAY，必填
            map.put("product_code", "FAST_INSTANT_TRADE_PAY");
            //付款金额，必填
            map.put("total_amount", price);
            //订单名称，必填
            map.put("subject", name);
            String string = JSONObject.toJSONString(map);
            log.info("--------" + string);
            request.setBizContent(string);
            //创建订单
            createPayLog(outTradeNo, name, price);
            //异步通知
            request.setNotifyUrl(AlipayConfig.notify_url);
            //页面跳转
            request.setReturnUrl(AlipayConfig.return_url);
            String body = client.pageExecute(request).getBody();
            response.setContentType("text/html;charset=utf-8");
            response.getWriter().write(body);
            response.getWriter().flush();
            response.getWriter().close();
        } catch (Exception e) {
            log.error(e.getMessage());
        }
    }

    @Override
    public String notifyPay(HttpServletRequest request) {
        try {
            log.info("进入异步通知接口");
            //获取支付宝POST过来反馈信息
            Map<String, String> params = new HashMap<>();
            Map<String, String[]> requestParams = request.getParameterMap();
            for (String name : requestParams.keySet()) {
                String[] values = requestParams.get(name);
                String valueStr = "";
                for (int i = 0; i < values.length; i++) {
                    valueStr = (i == values.length - 1) ? valueStr + values[i]
                            : valueStr + values[i] + ",";
                }
                params.put(name, valueStr);
            }
            //调用SDK验证签名
            boolean signVerified = AlipaySignature.rsaCheckV1(params, AlipayConfig.alipay_public_key, AlipayConfig.charset, AlipayConfig.sign_type);

            /* 实际验证过程建议商户务必添加以下校验：
	            1、需要验证该通知数据中的out_trade_no是否为商户系统中创建的订单号，
	            2、判断total_amount是否确实为该订单的实际金额（即商户订单创建时的金额），
	            3、校验通知中的seller_id（或者seller_email) 是否为out_trade_no这笔单据的对应的操作方（有的时候，一个商户可能有多个seller_id/seller_email）
	            4、验证app_id是否为该商户本身。
	        */
            if (signVerified) {//验证成功
                //商户订单号
                String out_trade_no = request.getParameter("out_trade_no");
                //支付宝交易号
                String trade_no = request.getParameter("trade_no");
                //交易状态
                String trade_status = request.getParameter("trade_status");
                //支付金额
                String total_amount = request.getParameter("total_amount");

                if (StringUtils.isEmpty(out_trade_no)) {
                    log.error("商户订单号为空");
                    return "fail";
                }
                if (StringUtils.isEmpty(trade_no)) {
                    log.error("支付宝交易号为空");
                    return "fail";
                }
                if (StringUtils.isEmpty(total_amount)) {
                    log.error("支付金额为空");
                    return "fail";
                }
                if (StringUtils.isEmpty(trade_status)) {
                    log.error("交易状态为空");
                    return "fail";
                }

                //比对金额和交易号是否一致
                PayLog payLog = new LambdaQueryChainWrapper<>(payLogDao).eq(PayLog::getOutTradeNo, out_trade_no).one();
                if (payLog == null) {
                    log.error("订单编号不存在！【" + out_trade_no + "】");
                    return "fail";
                } else {
                    if (payLog.getTotalAmount().compareTo(new Double(total_amount)) != 0) {
                        log.error("支付金额不同！【" + total_amount + "】" + "【" + payLog.getTotalAmount() + "】");
                        return "fail";
                    }
                }
                //参数验证合法，将交易号与状态更新到本地数据库
                payLog.setTradeNo(trade_no);
                payLog.setStatus(Constant.PAY_STATUS_PAID);
                payLog.setPayTime(new Date());
                payLogDao.updateById(payLog);
                return "success";

            } else {//验证失败
                //调试用，写文本函数记录程序运行情况是否正常
//                String sWord = AlipaySignature.getSignCheckContentV1(params);
//                AlipayConfig.logResult(sWord);
                return "fail";
            }
        } catch (Exception e) {
            log.error(e.getMessage());
        }
        return "fail";
    }

    @Override
    public String queryPayLog(HttpServletRequest request) {
        try {
            //获得初始化的AlipayClient
            AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
            //设置请求参数
            AlipayTradeQueryRequest alipayRequest = new AlipayTradeQueryRequest();
            //商户订单号，商户网站订单系统中唯一订单号
            String out_trade_no = request.getParameter("out_trade_no");
            //支付宝交易号
            String trade_no = request.getParameter("trade_no");
            //请二选一设置,两者都存在优先取trade_no
            Map<String, Object> map = new HashMap<>();
            map.put("out_trade_no", out_trade_no);
            map.put("trade_no", trade_no);
            alipayRequest.setBizContent(JSONObject.toJSONString(map));
            //请求
            String result = alipayClient.execute(alipayRequest).getBody();
            log.info("交易查询:【" + result + "】");
            return result;
        } catch (Exception e) {
            log.error(e.getMessage());
            return e.getMessage();
        }
    }

    @Override
    public String closePayLog(HttpServletRequest request) {
        try {
            //获得初始化的AlipayClient
            AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
            //设置请求参数
            AlipayTradeCloseRequest alipayRequest = new AlipayTradeCloseRequest();
            //商户订单号，商户网站订单系统中唯一订单号
            String out_trade_no = request.getParameter("out_trade_no");
            //支付宝交易号
//            String trade_no = request.getParameter("trade_no");
            //请二选一设置
            alipayRequest.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\"}");
            //请求
            String result = alipayClient.execute(alipayRequest).getBody();
            log.info("返回数据------>" + result);
            JSONObject jsonObject = JSONObject.parseObject(result).getJSONObject("alipay_trade_close_response");
            Long code = Long.valueOf(jsonObject.get("code").toString());
            //关闭成功，同步数据状态
            if (code.compareTo(10000L) == 0) {
                PayLog payLog = new LambdaQueryChainWrapper<>(payLogDao).eq(PayLog::getOutTradeNo, out_trade_no).one();
                payLog.setStatus(Constant.ABLE_PRAISE_CLOSED);
                payLog.setUpdateTime(new Date());
                payLogDao.updateById(payLog);
            }
            return result;
        } catch (Exception e) {
            log.error(e.getMessage());
            return e.getMessage();
        }
    }

    @Override
    public String refundPay(HttpServletRequest request) {
        try {
            //获得初始化的AlipayClient
            AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
            //设置请求参数
            AlipayTradeRefundRequest alipayRequest = new AlipayTradeRefundRequest();
            //商户订单号，商户网站订单系统中唯一订单号
            String out_trade_no = request.getParameter("out_trade_no");
            //支付宝交易号
//            String trade_no = request.getParameter("trade_no");
            //请二选一设置
            //需要退款的金额，该金额不能大于订单金额，必填
            String refund_amount = request.getParameter("refund_amount");
            //退款的原因说明
            String refund_reason = request.getParameter("refund_reason");
            //标识一次退款请求，同一笔交易多次退款需要保证唯一，如需部分退款，则此参数必传
            String out_request_no = UUIDUtil.getUUID();
            //判断是否可以退款
            int payCode = refundAliPay(out_trade_no, refund_amount);

            if (payCode == 1) {
                return "退款金额不能大于可退款金额";
            } else if (payCode == 2) {
                return "订单已关闭，不能退款";
            } else if (payCode == 3) {
                return "订单未支付，不能退款";
            } else if (payCode == 4) {
                return "订单已全部退款";
            } else {
                alipayRequest.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\","
                        + "\"refund_amount\":\"" + refund_amount + "\","
                        + "\"refund_reason\":\"" + refund_reason + "\","
                        + "\"out_request_no\":\"" + out_request_no + "\"}");
                //请求
                String result = alipayClient.execute(alipayRequest).getBody();
                log.info("退款信息------>" + result);
                JSONObject jsonObject = JSONObject.parseObject(result).getJSONObject("alipay_trade_refund_response");
                Long code = Long.valueOf(jsonObject.get("code").toString());
                //退款成功，更新数据库并保存退款记录
                if (code.compareTo(10000L) == 0) {
                    //查询退款记录
                    List<RefundLog> refundLogList = new LambdaQueryChainWrapper<>(refundLogDao).eq(RefundLog::getOutTradeNo, out_trade_no).list();
                    double refundTotalAmount = 0.00;
                    BigDecimal d1 = new BigDecimal(refundTotalAmount);
                    if (refundLogList.size() > 0) {
                        for (RefundLog aRefundLogList : refundLogList) {
                            BigDecimal d2 = new BigDecimal(aRefundLogList.getRefundAmount());
                            refundTotalAmount += d1.add(d2).doubleValue();
                        }
                    }
                    //插入退款记录
                    RefundLog refundLog = new RefundLog();
                    refundLog.setOutTradeNo(out_trade_no);
                    refundLog.setRefundNo(out_request_no);
                    refundLog.setRefundAmount(parseDouble(refund_amount));
                    refundLog.setRefundInfo(refund_reason);
                    refundLog.setRefundTime(new Date());
                    refundLogDao.insert(refundLog);
                    //更新支付记录
                    refundTotalAmount += parseDouble(refund_amount);
                    PayLog payLog = new LambdaQueryChainWrapper<>(payLogDao).eq(PayLog::getOutTradeNo, out_trade_no).one();
                    payLog.setUpdateTime(new Date());
                    if (refundTotalAmount == payLog.getTotalAmount()) {
                        payLog.setStatus(Constant.PAY_STATUS_ALL_REFUNDED);
                    } else {
                        payLog.setStatus(Constant.PAY_STATUS_REFUNDED);
                    }
                    payLog.setRefundTotalAmount(refundTotalAmount);
                    payLogDao.updateById(payLog);
                }
                return result;
            }
        } catch (Exception e) {
            log.error(e.getMessage());
            return e.getMessage();
        }

    }

    @Override
    public String refundQuery(HttpServletRequest request) {
        try {
            //获得初始化的AlipayClient
            AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
            //设置请求参数
            AlipayTradeFastpayRefundQueryRequest alipayRequest = new AlipayTradeFastpayRefundQueryRequest();
            //商户订单号，商户网站订单系统中唯一订单号
            String out_trade_no = request.getParameter("out_trade_no");
            //请求退款接口时，传入的退款请求号，如果在退款请求时未传入，则该值为创建交易时的外部交易号，必填
            //此处为退款记录编号
            String out_request_no = request.getParameter("refund_no");

            alipayRequest.setBizContent("{\"out_trade_no\":\"" + out_trade_no + "\","
                    + "\"out_request_no\":\"" + out_request_no + "\"}");
            //请求
            String result = alipayClient.execute(alipayRequest).getBody();
            log.info("退款记录------>" + result);
            return result;
        } catch (Exception e) {
            log.error(e.getMessage());
            return e.getMessage();
        }
    }

    @Override
    public String downloadPayLog(HttpServletRequest request) {
        try {
            //获得初始化的AlipayClient
            AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.gatewayUrl, AlipayConfig.app_id, AlipayConfig.merchant_private_key, "json", AlipayConfig.charset, AlipayConfig.alipay_public_key, AlipayConfig.sign_type);
            AlipayDataDataserviceBillDownloadurlQueryRequest alipayRequest = new AlipayDataDataserviceBillDownloadurlQueryRequest();
            //账单类型 trade：商户基于支付宝交易收单的业务账单；signcustomer：基于商户支付宝余额收入及支出等资金变动的帐务账单。
            String bill_type = request.getParameter("bill_type");
            //账单时间,必填
            String bill_date = request.getParameter("bill_date");
            alipayRequest.setBizContent("{\"bill_type\":\"" + bill_type + "\","
                    + "\"bill_date\":\"" + bill_date + "\"}");
            //请求
            String result = alipayClient.execute(alipayRequest).getBody();
            log.info("账单记录------>" + result);
            return result;
        } catch (Exception e) {
            log.error(e.getMessage());
            return e.getMessage();
        }
    }

    @Override
    public List<RefundLog> localPayLogQuery() {
        return refundLogDao.selectList(null);
    }

    /**
     * 创建订单，保存到本地数据库
     *
     * @param outTradeNo 商户订单号
     * @param name       订单名称
     * @param price      付款金额
     */
    private void createPayLog(String outTradeNo, String name, String price) {
        PayLog payLog = new PayLog();
        payLog.setOutTradeNo(outTradeNo);
        payLog.setSubject(name);
        payLog.setTotalAmount(parseDouble(price));
        payLog.setStatus(Constant.PAY_STATUS_WAITING);
        payLog.setCreateTime(new Date());
        payLogDao.insert(payLog);
    }

    /**
     * 判断是否可以退款
     *
     * @param out_trade_no  订单号
     * @param refund_amount 退款金额
     * @return int
     */
    private int refundAliPay(String out_trade_no, String refund_amount) {
        List<RefundLog> refundLogList = new LambdaQueryChainWrapper<>(refundLogDao).eq(RefundLog::getOutTradeNo, out_trade_no).list();
        double refundTotalAmount = 0.00;
        BigDecimal d1 = new BigDecimal(refundTotalAmount);
        if (refundLogList.size() > 0) {
            for (RefundLog aRefundLogList : refundLogList) {
                BigDecimal d2 = new BigDecimal(aRefundLogList.getRefundAmount());
                refundTotalAmount += d1.add(d2).doubleValue();
            }
        }
        //查询支付记录
        PayLog payLog = new LambdaQueryChainWrapper<>(payLogDao).eq(PayLog::getOutTradeNo, out_trade_no).one();
        //退款金额
        double refundAmount = parseDouble(refund_amount);
        //支付总金额
        double totalAmount = payLog.getTotalAmount();
        //可退款金额
        BigDecimal b1 = new BigDecimal(totalAmount);
        BigDecimal b2 = new BigDecimal(refundTotalAmount);
        double enableAmount = b1.subtract(b2).doubleValue();
        if (payLog.getStatus().equals(Constant.ABLE_PRAISE_CLOSED)) {
            //订单已关闭，不能退款
            return 2;
        } else if (payLog.getStatus().equals(Constant.PAY_STATUS_WAITING)) {
            //订单未支付，不能退款
            return 3;
        } else if (payLog.getStatus().equals(Constant.PAY_STATUS_ALL_REFUNDED)) {
            //订单已全部退款
            return 4;
        } else {
            //表示可以退款
            if (refundAmount > enableAmount) {
                //退款金额不能大于可退款金额
                return 1;
            }
            return 0;
        }

    }
}
